package ltd.nullpointer.tcp.core.serialize;

import com.google.common.collect.ImmutableMap;
import com.boot2.core.HlAssert;
import com.boot2.core.utils.ByteUtils;
import com.boot2.core.utils.ReflectUtil;
import com.boot2.core.utils.StringUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import lombok.extern.apachecommons.CommonsLog;
import ltd.nullpointer.tcp.core.annotation.*;
import ltd.nullpointer.tcp.core.constant.TCPEnum;
import ltd.nullpointer.tcp.core.message.AbstractTCPMessage;
import ltd.nullpointer.tcp.core.resolver.AbstractTCPMessageClassFieldEntry;
import ltd.nullpointer.tcp.core.serialize.parser.*;

import javax.persistence.Table;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.util.*;

/**
 * @author zhangweilin
 * @Description: 序列化默认实现
 * @date 2019/12/14
 */
@CommonsLog
public abstract class AbstractTCPMessageSerializer extends AbstractTCPMessageClassFieldEntry implements MessageSerializer {

    /**
     * 序列化插件注册
     */
    protected Map<Class<?>, SerializerParser<?>> serializerParserMap;

    /**
     * 是否需要加解密
     */
    private Boolean isNeedEncrypt;

    /**
     * 接受外部传参
     *
     * @param isNeedEncrypt
     */
    public void setIsNeedEncrypt(Boolean isNeedEncrypt) {
        this.isNeedEncrypt = isNeedEncrypt;
    }

    /**
     * 默认不走加密通道
     *
     * @return
     */
    @Override
    public Boolean isNeedEncrypt() {
        return isNeedEncrypt == null ? false : isNeedEncrypt;
    }


    /**
     * 奇怪，通过jaavaassit生成的子类，调用set居然子类获取不到，换成set以外的却可以，故改成reg
     *
     * @param serializerParserMap
     */
    public void regSerializerParserMap(Map<Class<?>, SerializerParser<?>> serializerParserMap) {
        this.serializerParserMap = serializerParserMap;
    }

    /**
     * 将包序列化成字节序列
     *
     * @param abstractTCPMessage
     * @return
     */
    @Override
    public ByteBuf serialize(AbstractTCPMessage<?> abstractTCPMessage) {
        TreeMap<Integer, byte[]> byteArrMap = new TreeMap<>();
        //字节总长度
        int byteTotalLength = 0;
        Object payload = abstractTCPMessage.getPayload();

        //==beforeSerialize
        TcpDownMessage tcpDownMessage = beforeSerialize0(payload);
        //==/beforeSerialize

        Map<String, Object> paramMap = new HashMap<>();
        //解析头定义
        TcpHeader header = tcpDownMessage.header();
        TCPEnum.SerializerType serializerType = header.serializerType();
        Object value = header.value();
        int length = ((String) value).length() / 2;
        String dateFormat = null;
        String calcExpression = null;
        String charsetName = "GB2312";
        byte[] byteArr = doSerializer(value, payload, serializerType, paramMap);
        byteArrMap.put(header.index(), byteArr);
        byteTotalLength += byteArr.length;

        //解析命令字（命令类型)
        TcpType type = tcpDownMessage.type();
        serializerType = type.serializerType();
        value = type.value();
        length = ((String) value).length() / 2;
        paramMap.put("length", length);
        byteArr = doSerializer(value, payload, serializerType, paramMap);
        byteArrMap.put(type.index(), byteArr);
        byteTotalLength += byteArr.length;

        TcpReservedWord tcpReservedWord = tcpDownMessage.reservedWord();
        serializerType = tcpReservedWord.serializerType();
        value = tcpReservedWord.value();
        length = ((String) value).length() / 2;
        paramMap.put("length", length);
        byteArr = doSerializer(value, payload, serializerType, paramMap);
        byteArrMap.put(tcpReservedWord.index(), byteArr);
        byteTotalLength += byteArr.length;

        //正文前，记下当前位置，正文部分的偏移量，需要加上当前位置
        int diff = byteTotalLength;
        //解析正文
        String headerHexStr = header.value();
        String typeHexStr = type.value();
        String msgCode = headerHexStr + typeHexStr;
        ClassFieldEntry classListMap = classFieldEntryMap.get(msgCode);
        HlAssert.notNull(classListMap, "msgCode " + msgCode + " 无效");
        //此是已经排好序的
        int dataLength = 0;
        List<Field> fieldList = classListMap.getFieldList();
        for (int i = 0; i < fieldList.size(); i++) {
            Field field = fieldList.get(i);
            TcpDownOffset tcpDownOffset = field.getAnnotation(TcpDownOffset.class);
            int start = tcpDownOffset.start();
            serializerType = tcpDownOffset.serializerType();
            calcExpression = tcpDownOffset.calcExpression();
            charsetName = tcpDownOffset.charsetName();
            int end = tcpDownOffset.end();
            dateFormat = tcpDownOffset.dateFormat();
            length = end - start + 1;
            //计算数据块总长度
            dataLength += length;

            String fieldName = field.getName();
            value = ReflectUtil.getFieldValue(payload, field);
            paramMap.put("length", length);
            paramMap.put("dateFormat", dateFormat);
            paramMap.put("calcExpression", calcExpression);
            paramMap.put("charsetName", charsetName);
            byteArr = doSerializer(value, payload, serializerType, paramMap);
            byteArrMap.put(diff + start, byteArr);
            byteTotalLength += byteArr.length;
        }
        //-/正文结束

        TcpMessageLength lengthMessage = tcpDownMessage.length();
        serializerType = lengthMessage.serializerType();
//        value = "数据块的长度".length();  //应该是字节个数
        value = dataLength;
        int byteLength = lengthMessage.byteLength();
        paramMap.put("length", byteLength);
        byteArr = doSerializer(value, payload, serializerType, paramMap);
        byteArrMap.put(lengthMessage.index(), byteArr);
        byteTotalLength += byteArr.length;

        //需要校验的部分
        ByteBuffer messageByteBuffer = ByteBuffer.allocate(byteTotalLength);
        byteArrMap.forEach((k, v) -> {
            messageByteBuffer.put(v);
        });
        //计算校验码,不能
        byte[] allByteArr = messageByteBuffer.array();
        //上一行生成过数组，必须有此语句重置下位置
        messageByteBuffer.rewind();
        String bcc = ByteUtils.getBCC(allByteArr);
        log.debug("下发算出的 bcc = " + bcc);

        byte[] bccByteArr = ByteUtils.hexString2Bytes(bcc);

        //正文+校验码
        int capacity = allByteArr.length + bccByteArr.length;
//        ByteBuffer totalByteBuffer = ByteBuffer.allocate(capacity);
//        totalByteBuffer.put(messageByteBuffer);
//        totalByteBuffer.put(bccByteArr);//todo ?? 奇怪，添加ByteBuffer无效

        ByteBuf byteBuf = PooledByteBufAllocator.DEFAULT.directBuffer(capacity);
        byteBuf.writeBytes(messageByteBuffer);
        byteBuf.writeBytes(bccByteArr);
        return byteBuf;
    }

    /**
     *beforeSerialize0
     * @param payload
     * @return
     */
    private TcpDownMessage beforeSerialize0(Object payload) {
        Class<?> clazz = payload.getClass();
        Table table = clazz.getAnnotation(Table.class);
        TcpDownMessage tcpDownMessage = clazz.getAnnotation(TcpDownMessage.class);
        boolean persistence = true;
        if (tcpDownMessage != null) {
            persistence = tcpDownMessage.isPersistence();
        }
        if (table != null && persistence) {
            beforeSerialize(payload);
        }
        return tcpDownMessage;
    }

    /**
     * 序列化前的处理
     *
     * @param payload
     */
    protected void beforeSerialize(Object payload) {

    }

    private byte[] doSerializer(Object value, Object payload, TCPEnum.SerializerType serializerType, Map<String, Object> paramMap0) {
        Integer length = null;
        String dateFormat = null;
        String calcExpression = null;
        String charsetName = null;
        if (null != paramMap0) {
            length = (Integer) paramMap0.get("length");
            dateFormat = (String) paramMap0.get("dateFormat");
            calcExpression = (String) paramMap0.get("calcExpression");
            charsetName = (String) paramMap0.get("charsetName");
        }

        byte[] valueByteArr = null;
        switch (serializerType) {
            case hexString2HexBytes:
                if (StringUtils.isEmpty(dateFormat)) {
                    SerializerParser<String> serializerParser = (SerializerParser<String>) serializerParserMap.get(HexString2HexBytesSerializerParser.class);
                    valueByteArr = serializerParser.serialize((String) value, payload, null);
                    return valueByteArr;
                } else {
                    SerializerParser<Date> dateSerializerParser = (SerializerParser<Date>) serializerParserMap.get(DateSerializerParser.class);
                    valueByteArr = dateSerializerParser.serialize((Date) value, payload, ImmutableMap.of("dateFormat", dateFormat, "charsetName", charsetName));
                    return valueByteArr;
                }
            case int2HexByte:
            case unsignedInt2HexByte:
                Object value2 = value;
                if (StringUtils.isNotEmpty(calcExpression)) {
                    SerializerParser<Number> serializerParser = (SerializerParser<Number>) serializerParserMap.get(CalcExpressionSerializerParser.class);
                    Map<String, Object> paramMap = new HashMap<>(2);
                    paramMap.put("expression", calcExpression);
                    serializerParser.serialize((Number) value, payload, paramMap);
                    //计算的结果在map中
                    value2 = paramMap.get("value");
                }
                SerializerParser<Integer> serializerParser1 = (SerializerParser<Integer>) serializerParserMap.get(UnsignedInt2HexByteSerializerParser.class);
                valueByteArr = serializerParser1.serialize((Integer) value2, payload, ImmutableMap.of("byteLength", length));
                return valueByteArr;
            case ascii:
                SerializerParser<String> serializerParser2 = (SerializerParser<String>) serializerParserMap.get(AsciiSerializerParser.class);
                valueByteArr = serializerParser2.serialize((String) value, payload, ImmutableMap.of("charsetName", charsetName));
                return valueByteArr;
            case manually:

                break;
            default:
                break;
        }
        return null;
    }

}
